const Path = require("path");

const fs = require("fs");
const {Optional} = require("../array/listProcess");
const os = require('os');
const {FileEntityError, execute} = require("./common");
const system = os.type();


class System {
    async readRoot() {
        throw new FileEntityError('No implementation method [readRoot]')
    }

    async open(path) {
        throw new FileEntityError('No implementation method [open]')
    }

    static getInstance() {
        if (system.includes("Windows")) {
            return new Windows();
        } else if (system.includes("Linux")) {
            return new Linux();
        } else {
            throw new FileEntityError('Currently, only Windows and Linux systems are supported.')
        }
    }
}

class Windows extends System {

    async readRoot() {
        const res = await execute("wmic logicaldisk where drivetype=3 get deviceid,volumename");
        const names = res.split(/\r\r\n/).filter(Boolean).map(i => i.trim().split(' ').filter(Boolean));
        names.splice(0, 1);
        // Path join 传盘符会有问题,要多传一个'.'
        const result = []
        for (let i = 0; i < names.length; i++) {
            const [path, name] = names[i];
            const fileEntity = FileEntity.of(path, '.');
            if (fileEntity) {
                fileEntity.type = 'disk'
                fileEntity.fileName = name
                result.push(fileEntity)
            }
        }
        return result;
    }

    async open(path) {
        await execute(`explorer file://${path}`)
    }
}

class Linux extends System {

    async readRoot() {
        return FileEntity.fetch('/');
    }


    async open(path) {
        await execute(`nautilus ${path}`)
    }
}


class FileEntity {
    /**
     * @property {'folder'|'file'|'disk'} type
     */
    fileName
    filePath
    type
    size
    lastUpdateTime
    createTime
    #removed = false

    ino

    getParentFolderPath() {
        return FileEntity.getParentFolderPathByPath(this.filePath);
    }

    static getParentFolderPathByPath(path) {
        const strings = path.split("/");
        strings.splice(-1, 1)
        return strings.join("/")
    }

    fetch(isFolder = false) {
        return FileEntity.fetch(this.filePath, isFolder);
    }

    /**
     * @return FileEntity[]
     */
    static fetch(path, isFolder = false) {
        const readdirSync = fs.readdirSync(`${path}/`);
        let results = [];
        readdirSync.forEach(fileName => {
            try {
                const filePath = `${path}/${fileName}`;
                const fileEntity = FileEntity.of(filePath);
                if (fileEntity) {
                    results.push(fileEntity);
                }
            } catch (e) {
                console.error(`错误`);
            }
        })
        if (isFolder) {
            results = results.filter(value => value.type === 'folder')
        }
        return results;
    }

    static #system = System.getInstance();

    static async fetchWithDisk(path, isFolder = false) {
        if (path) {
            return FileEntity.fetch(path, isFolder)
        } else {
            return await this.#system.readRoot();
        }
    }

    getCopyNewPath(targetPath) {
        const [name, ext] = this.getNameAndExt();
        const fileEntities = FileEntity.fetch(targetPath);
        const sameNameCount = fileEntities.filter(value => {
            const [valueName, valueExt] = value.getNameAndExt();
            const relateCopied = valueName.startsWith(name) && /\([1-9]+\)$/.test(valueName);
            return valueExt === ext && (name === valueName || relateCopied);
        }).length;
        if (this.isFile()) {
            return FileEntity.pathJoin(targetPath, `${name}${sameNameCount > 0 ? `(${sameNameCount})` : ''}${ext}`);
        } else {
            return FileEntity.pathJoin(targetPath, `${this.fileName}${sameNameCount > 0 ? `(${sameNameCount})` : ''}`);
        }
    }

    getMoveNewPath(targetPath) {
        const fileEntities = FileEntity.fetch(targetPath);
        /*如果目标文件夹已存在文件,则报错*/
        if (fileEntities.some(value => value.fileName === this.fileName)) {
            throw new FileEntityError(`文件夹[${targetPath}]已存在文件或文件夹${this.fileName}`)
        }
        return FileEntity.pathJoin(targetPath, this.fileName);
    }

    getNameAndExt() {
        if (this.isFile()) {
            const lastIndexOf = this.fileName.lastIndexOf('.');
            return [this.fileName.slice(0, lastIndexOf), this.fileName.slice(lastIndexOf)];
        } else {
            /*后缀名如果没有,需要返回空字符串,因为后面正则要用,如果不返回会显示undefined*/
            return [this.fileName, '']
        }
    }

    /**
     * @return FileEntity
     */
    static of(...filePaths) {
        let filePath = this.getPath(Path.join(...filePaths))
        const split = filePath.split("/");
        const fileName = split[split.length - 1]
        if (!fs.existsSync(filePath)) {
            return null;
        }
        const stats = fs.lstatSync(filePath);
        const isDirectory = stats.isDirectory();
        const fileEntity = new FileEntity();
        fileEntity.size = stats.size;
        fileEntity.fileName = fileName;
        fileEntity.filePath = filePath;
        fileEntity.type = isDirectory ? 'folder' : 'file'
        fileEntity.createTime = stats.birthtime.getTime();
        fileEntity.lastUpdateTime = stats.mtime.getTime();
        fileEntity.ino = stats.ino
        return fileEntity;
    }

    static ofNullable(...filePaths) {
        const fileEntity = FileEntity.of(...filePaths);
        return Optional.of(fileEntity)
    }

    getFile(path) {
        return FileEntity.of(FileEntity.pathJoin(this.filePath, path));
    }

    remove() {
        if (this.#removed) {
            throw new FileEntityError('文件已删除')
        }
        if (this.isFile()) {
            fs.rmSync(this.filePath)
        } else if (this.isDirectory()) {
            const fileEntities = FileEntity.fetch(this.filePath);
            for (let fileEntity of fileEntities) {
                fileEntity.remove()
            }
            fs.rmdirSync(this.filePath)
        }
        this.#removed = true;
    }

    isDirectory() {
        return this.type === 'folder';
    }

    isFile() {
        return this.type === 'file';
    }

    isDisk() {
        return this.type === 'disk'
    }

    move(targetPath) {
        FileEntity.validateDir(targetPath)
        const newPath = this.getMoveNewPath(targetPath);
        if (this.isFile()) {
            fs.renameSync(this.filePath, newPath);
        } else if (this.isDirectory()) {
            fs.mkdirSync(newPath);
            const fileEntities = FileEntity.fetch(this.filePath);
            for (let fileEntity of fileEntities) {
                fileEntity.move(newPath)
            }
            fs.rmdirSync(this.filePath)
        }
        this.refresh(newPath)
    }

    rename(newName) {
        const parentFolderPath = this.getParentFolderPath();
        const fileEntities = FileEntity.fetch(parentFolderPath);
        const isExit = fileEntities.some(value => value.fileName === newName && value.filePath !== this.filePath);
        if (isExit) {
            throw new FileEntityError(`新文件名已存在`)
        }
        const newPath = `${parentFolderPath}/${newName}`
        fs.renameSync(this.filePath, newPath);
        this.refresh(newPath);
    }

    refresh(newPath) {
        const fileEntity = FileEntity.of(newPath);
        this.filePath = fileEntity.filePath
        this.type = fileEntity.type;
        this.size = fileEntity.size
        this.fileName = fileEntity.fileName
        this.createTime = fileEntity.createTime
        this.lastUpdateTime = fileEntity.lastUpdateTime
    }

    copy(targetPath) {
        FileEntity.validateDir(targetPath);
        const newPath = this.getCopyNewPath(targetPath);
        if (this.isFile()) {
            fs.copyFileSync(this.filePath, newPath)
        } else if (this.isDirectory()) {
            fs.mkdirSync(newPath);
            const fileEntities = FileEntity.fetch(this.filePath);
            for (let fileEntity of fileEntities) {
                fileEntity.copy(newPath)
            }
        }
        this.refresh(newPath)
    }

    static validateDir(targetPath) {
        if (!fs.lstatSync(targetPath).isDirectory()) {
            throw new FileEntityError(`目标路径不是文件夹`);
        }
    }

    static pathJoin(...paths) {
        const s = Path.join(...paths);
        return this.getPath(s);
    }

    static getPath(s) {
        return s.replace(/\\/g, '/');
    }

    createChildFolder(name) {
        if (!this.isDirectory()) {
            throw new FileEntityError('当前FileEntity不是文件夹')
        }
        const childFolderPath = FileEntity.pathJoin(this.filePath, name);
        fs.mkdirSync(childFolderPath)
        return FileEntity.of(childFolderPath)
    }

    createFile(name, fileContent) {
        if (!this.isDirectory()) {
            throw new FileEntityError('当前FileEntity不是文件夹')
        }
        const childFilePath = FileEntity.pathJoin(this.filePath, name);
        fs.writeFileSync(childFilePath, fileContent)
        return FileEntity.of(childFilePath)
    }

    readAsBuffer() {
        if (!this.isFile()) {
            throw new FileEntityError('当前FileEntity不是文件')
        }
        return fs.readFileSync(this.filePath);
    }

    async open() {
        await FileEntity.#system.open(this.filePath)
    }

    json() {
        return JSON.parse(this.readAsBuffer().toString())
    }

    text() {
        return this.readAsBuffer().toString();
    }

    writeJson(json) {
        fs.writeFileSync(this.filePath, JSON.stringify(json))
    }

    writeText(text) {
        fs.writeFileSync(this.filePath, text)
    }


}

module.exports = {
    FileEntity
}